package cs301.msflags;

import cs301.game.GameAction;
import cs301.game.GameConfig;
import cs301.game.LocalGame;

/**
 * @author John Olennikov
 * @author Kyanne Mayfield
 * @author Allie Seibert
 * @author Emilia Holbik
 * @version 12/7/2012
 * 
 *          This class knows how to play the game. The data in this class
 *          represents the state of the game. A partial state of the game can be
 *          seen by each of the players or a full state of the game can be seen
 *          by the MinesweeperFlagsMainActivity class.
 */

public class MSFlagsGame extends LocalGame {

	/**
	 * The board for the current game
	 */

	private Board board;

	/**
	 * Turns true if a player uncovers a mine
	 */

	private boolean uncoveredMine = false;

	/**
	 * The total number of mines laid out in the board
	 */

	private int totalNumMines = 0;

	/**
	 * The width of the board, i.e., the number of spaces in any row of the
	 * board
	 */

	private int boardWidth = 0;

	/**
	 * The height of the board, i.e., the number of spaces in any column of the
	 * board
	 */

	private int boardHeight = 0;

	/**
	 * Keeps track of whether the player has uncovered a space and can click the
	 * "end turn" button.
	 */

	private boolean uncoveredSpace = false;

	/**
	 * Keeps track of how many flags player 1 has available to place.
	 */
	private int player1FlagsLeft = 0;

	/**
	 * Keeps track of how many flags player 2 has available to place.
	 */
	private int player2FlagsLeft = 0;

	/**
	 * Provides the ability to quit the game
	 */
	private boolean quitGame = false;
	
	/**
	 * Keeps track of whether a turn is the first turn or not
	 */

	private boolean isFirstMove;


	/**
	 * Constructor 
	 * 
	 * Creates a new Minesweeper Flags game.
	 * 
	 * @param initConfig  the initial set up of players
	 * @param initTurn  who initially has the turn
	 */

	public MSFlagsGame(GameConfig initConfig, int initTurn) {
		super(initConfig, initTurn);
		this.whoseTurn = initTurn;
		this.isFirstMove = true;
	}

	/**
	 * Gets the current game state from the perspective of the
	 * given player.
	 * 
	 * @param playerIndex  the index of the player by whom the gamestate will be seen
	 * @return the current gamestate
	 */

	@Override
	public LocalGame getPlayerState(int playerIndex) {
		// Either player gets the current gamestate.
		MSFlagsGame copy = new MSFlagsGame(getConfig(), this.whoseTurn);

		copy.board = this.board;
		copy.player1FlagsLeft = this.player1FlagsLeft;
		copy.player2FlagsLeft = this.player2FlagsLeft;
		copy.totalNumMines = this.totalNumMines;
		copy.boardWidth = this.boardWidth;
		copy.boardHeight = this.boardHeight;
		copy.uncoveredSpace = this.uncoveredSpace;
		copy.uncoveredMine = this.uncoveredMine;
		copy.quitGame = this.quitGame;
		copy.isFirstMove = this.isFirstMove;
		return copy;
	}

	/**
	 * Applies the given game action
	 * 
	 * @param action  the given game action
	 */

	@Override
	public void applyAction(GameAction action) {
		// If a configure board action, set up the board.
		if (action instanceof MSFlagsConfigureBoardAction) {
			configureBoard(
					((MSFlagsConfigureBoardAction) action).getNumMines(),
					((MSFlagsConfigureBoardAction) action).getWidth(),
					((MSFlagsConfigureBoardAction) action).getHeight());
			
		// If an end turn action, update the state of the game.
		} else if (action instanceof MSFlagsEndTurnAction) {
			MSFlagsEndTurnAction endTurnAction = (MSFlagsEndTurnAction) action;
			this.board = endTurnAction.getBoard();
			if (this.whoseTurn == 0) {
				this.player1FlagsLeft = endTurnAction.getFlagsRemaining();
			} else {
				this.player2FlagsLeft = endTurnAction.getFlagsRemaining();
			}
			
			uncoveredMine = endTurnAction.getUncoveredMine();
			
			// Pass the turn to the next player.
			this.whoseTurn = 1 - this.whoseTurn;
			// The next player has not yet uncovered a space.
			uncoveredSpace = false;
			uncoveredMine = endTurnAction.getUncoveredMine();

			// Once an end turn action has been received, it's no longer the first turn.
			isFirstMove = false;
		} else if (action instanceof MSFlagsQuitGameAction) {
			System.exit(0);
		}
	}

	/**
	 * Configures the board.
	 * 
	 * @param numMines  the number of mines to be laid out in the board
	 * @param width  the width of the board
	 * @param height  the height of the board
	 */
	public void configureBoard(int numMines, int width, int height) {
		this.totalNumMines = numMines;
		this.boardWidth = width;
		this.boardHeight = height;

		// Initialize the board.
		board = new Board(this.totalNumMines, this.boardWidth, this.boardHeight);

		// Players have not placed any flags yet.
		this.player1FlagsLeft = totalNumMines;
		this.player2FlagsLeft = totalNumMines;
	}

	/**
	 * Determines whether the game is over
	 * 
	 * @return true if game is over, false if not.
	 */

	@Override
	public boolean isGameOver() {
		// If the user wants to quit, the game is over.
		if (this.quitGame) {
			return true;
		}
		// If a mine has been uncovered, the game is over.
		else if (this.uncoveredMine) {
			return true;
		}
		else {
			// Check if all spaces not containing mines have been uncovered.
			// If so, the game is over.
			if (this.board != null) {
				BoardSpace[][] boardSpaces = this.board.getBoardSpaceArray();
				int numUncovered = 0;
				for (int w = 0; w < boardSpaces.length; ++w) {
					for (int h = 0; h < boardSpaces[w].length; ++h) {

						if ((!boardSpaces[w][h].isCovered())) {
							++numUncovered;
						}
					}
				}
				if (numUncovered == (this.board.getTotalNumSpaces() - this.board
						.getNumMines())) {
					return true;
				}
			}

			// Otherwise, the game is not over.
			return false;
		}
	}

	/**
	 * Returns the Id of the winner of the game
	 * 
	 * @return game winner's id
	 */

	@Override
	public int getWinnerId() {
		// If a player uncovered a mine, the other player is the winner.
		if (this.uncoveredMine) {
			return this.whoseTurn();
		} else {
			
			int blueScore = this.board.getPlayerScore(0);
			int redScore = this.board.getPlayerScore(1);
			
			//Return the index of the player who placed more flags.
			if (redScore > blueScore) {
				return 1;
			} else {
				return 0;
			} // Note: in the case of a tie, the winner defaults to
			  // player 0 (blue player).
		}
	}

	/**
	 * Returns the game board.
	 * 
	 * @return the current board
	 */

	public Board getBoard() {
		return this.board;
	}
	
	/**
	 * Tells whether a turn is the first turn of the game.
	 * 
	 * @return true if this turn is the first turn of game, 
	 * 			false, otherwise.
	 */
	public boolean isFirstMove()
	{
		return isFirstMove;
	}

	/**
	 * Determines whether a player has uncovered a space yet during their turn.
	 * 
	 * @return true if player has already uncovered a space, false if not.
	 */

	public boolean hasUncovered() {
		return this.uncoveredSpace;
	}

	/**
	 * Sets uncoveredSpace variable based on whether the player has uncovered
	 * a space yet during their current turn or not.
	 * 
	 * @param status  true if the player has uncovered a space during their
	 *                current turn, false if not.
	 */
	public void setUncovered(boolean status) {
		this.uncoveredSpace = status;
	}

	/**
	 * Gets the number of flags the given player has available to place.
	 * 
	 * @param playerId  the player's id
	 *            
	 * @return the number of flags the given player has available to place.
	 */
	public int getFlagsRemaining(int playerId) {
		if (playerId == 0)
			return this.player1FlagsLeft;
		else
			return this.player2FlagsLeft;
	}

	/**
	 * Updates the number of flags the given player has available to place.
	 * 
	 * @param playerId  the player's id
	 * @param flags  the number of flags to add to the given player's number
	 *            	 of available flags.
	 */
	public void setFlagsRemaining(int playerId, int flags) {
		if (playerId == 0)
			this.player1FlagsLeft += flags;
		else
			this.player2FlagsLeft += flags;
	}

}